package com.woniuxy.lab.woniu_lab_boss.service.impl;
import java.time.LocalDateTime;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.woniuxy.lab.woniu_lab_boss.common.Shell;
import com.woniuxy.lab.woniu_lab_boss.controller.form.ModifyRangeForm;
import com.woniuxy.lab.woniu_lab_boss.controller.form.UploadRangeForm;
import com.woniuxy.lab.woniu_lab_boss.docker.DockerUtil;
import com.woniuxy.lab.woniu_lab_boss.docker.Image;
import com.woniuxy.lab.woniu_lab_boss.exception.RangeException;
import com.woniuxy.lab.woniu_lab_boss.exception.TeacherException;
import com.woniuxy.lab.woniu_lab_boss.exception.WebException;
import com.woniuxy.lab.woniu_lab_boss.model.Range;
import com.woniuxy.lab.woniu_lab_boss.dao.RangeDao;
import com.woniuxy.lab.woniu_lab_boss.model.Teacher;
import com.woniuxy.lab.woniu_lab_boss.service.RangeService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniuxy.lab.woniu_lab_boss.service.TeacherService;
import com.woniuxy.lab.woniu_lab_boss.service.dto.RangeDto;
import com.woniuxy.lab.woniu_lab_boss.util.PathUtil;
import com.woniuxy.lab.woniu_lab_boss.util.ShellUtil;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

import static com.woniuxy.lab.woniu_lab_boss.exception.WebExceptionCode.*;

/**
 * <p>
 * 靶场表 服务实现类
 * </p>
 *
 * @author Silas
 * @since 2023-03-15
 */
@Service
public class RangeServiceImpl extends ServiceImpl<RangeDao, Range> implements RangeService {
    public static final String ENABLE = "ENABLE";
    public static final String DISABLE = "DISABLE";
    @Resource
    private RangeDao rangeDao;
    @Resource
    private TeacherService teacherService;

    @Value("${docker.host}")
    private String host; // dockers服务器的ip

    @Value("${docker.username}")
    private String username;

    @Value("${docker.password}")
    private String password;

    @Value("${jar.host}")
    private String jarHost; // jar服务器的ip

    @Value("${jar.username}")
    private String jarUsername;

    @Value("${jar.password}")
    private String jarPassword;
//
//    @Value("${path.upload}")
//    private String uploadPath;

    @Override
    public IPage<RangeDto> queryRange(String condition, Integer page, Integer size) {
        IPage<Range> iPage = new Page<>(page == null ? 1 : page, size == null ? 4 : size);
        IPage<Range> page1 = rangeDao.selectPage(iPage, new QueryWrapper<Range>()
                .like("name", condition)
                .or()
                .like("image_name", condition)
                .or()
                .eq("level", condition)
                .or()
                .eq("score", condition)
                .or()
                .like("introduce", condition)
                .or()
                .eq("range_type",condition)
                .orderByDesc("upload_time")
        );
        return page1.convert(u -> {
            RangeDto rangeDto = new RangeDto();
            BeanUtils.copyProperties(u, rangeDto);
            return rangeDto;
        });
    }


    @Override
    public IPage<RangeDto> findAll(Integer page, Integer size) {
        IPage<Range> iPage = new Page<>(page == null ? 1 : page, size == null ? 4 : size);
        IPage<Range> page1 = rangeDao.selectPage(iPage, new QueryWrapper<Range>().orderByDesc("upload_time"));
        return page1.convert(u -> {
            RangeDto rangeDto = new RangeDto();
            BeanUtils.copyProperties(u, rangeDto);
            return rangeDto;
        });
    }

    @Override
    public IPage<RangeDto> queryByType(String condition, Integer page, Integer size) {
        IPage<Range> iPage = new Page<>(page == null ? 1 : page, size == null ? 4 : size);
        IPage<Range> page1 = rangeDao.selectPage(iPage, new QueryWrapper<Range>()
                .like("file_type", condition)
                .orderByDesc("upload_time")
        );
        return page1.convert(u -> {
            RangeDto rangeDto = new RangeDto();
            BeanUtils.copyProperties(u, rangeDto);
            return rangeDto;
        });
    }

    @Override
    public IPage<RangeDto> queryByWay(String condition, Integer page, Integer size) {
        IPage<Range> iPage = new Page<>(page == null ? 1 : page, size == null ? 4 : size);
        IPage<Range> page1 = rangeDao.selectPage(iPage, new QueryWrapper<Range>()
                .like("ctf_or_exper", condition).orderByDesc("upload_time")
        );
        return page1.convert(u -> {
            RangeDto rangeDto = new RangeDto();
            BeanUtils.copyProperties(u, rangeDto);
            return rangeDto;
        });
    }

    @Override
    public IPage<RangeDto> findMyRange(Integer page, Integer size) {

        Integer teacherId = teacherService.getTeacherId();

        Teacher teacher = teacherService.getById(teacherId);
        teacherIsExist(teacher);

        Page<Range> page1 = page(new Page<>(page == null ? 1 : page, size == null ? 4 : size), new QueryWrapper<Range>()
                .eq("status", ENABLE).eq("teacher_id", teacherId).orderByDesc("upload_time"));

        return page1.convert(u -> {
            RangeDto rangeDto = new RangeDto();
            BeanUtils.copyProperties(u, rangeDto);
            return rangeDto;
        });
    }

    /**
     * 验证讲师是否存在或者状态是否异常
     * @param teacher 讲师
     */
    private void teacherIsExist(Teacher teacher) {
        if (ObjectUtil.isEmpty(teacher) || !ENABLE.equals(teacher.getStatus())){
            throw new TeacherException(TEACHER_IS_NOT_EXISTS);
        }
    }


    @Override
    public RangeDto modifyRange(ModifyRangeForm modifyRangeForm) {
        //从token中获取teacherId
        Integer teacherId = teacherService.getTeacherId();
        Teacher teacher = teacherService.getById(teacherId);
        teacherIsExist(teacher);
        Range range = getById(modifyRangeForm.getRangeId());
        rangeIsExist(range);
        //判断该靶场是否为该讲师的靶场
        if (!teacher.getId().equals(range.getTeacherId())){
            throw new TeacherException(TEACHER_HAVE_NO_RIGHT_TO_THIS_RANGE);
        }
        Range range1 = BeanUtil.copyProperties(modifyRangeForm, Range.class);
        range1.setFileType(range.getFileType());
        range1.setFilePath(range.getFilePath());
        range1.setImageId(range.getImageId());
        range1.setImageName(range.getImageName());
        range1.setRangeType(range.getRangeType());
        range1.setOptCount(range.getOptCount());
        range1.setStatus(range.getStatus());
        range1.setCtfOrExper(range.getCtfOrExper());
        range1.setPassCount(range.getPassCount());
        range1.setCreateCount(range.getCreateCount());
        range1.setUploadTime(LocalDateTime.now());
        range1.setPort(range.getPort());
        range1.setPath(range.getPath());

        updateById(range1);


        return BeanUtil.copyProperties(range1, RangeDto.class);
    }

    /**
     * 验证靶场是否存在或者状态是否异常
     * @param range 靶场
     */
    private void rangeIsExist(Range range) {
        if (ObjectUtil.isEmpty(range) || !ENABLE.equals(range.getStatus())){
            throw new RangeException(RANGE_IS_NOT_EXISTS);
        }
    }

    @Override
    public void downRange(Integer rangeId) {
        //FIXME 目前该方法只修改了靶场的状态
        Range range = getById(rangeId);
        rangeIsExist(range);
        range.setStatus(DISABLE);
        updateById(range);

    }

    @Override
    public IPage<RangeDto> searchMyRange(Integer page, Integer size, String keyword) {
        Integer teacherId = teacherService.getTeacherId();

        IPage<Range> myPage = new Page<>(page, size);
        IPage<Range> iPage = this.page(myPage, new QueryWrapper<Range>()
                .like("name", keyword)
                .eq("teacher_id", teacherId)
                .orderByDesc("upload_time"));
        return iPage.convert(u -> {
            RangeDto rangeDto = new RangeDto();
            BeanUtils.copyProperties(u, rangeDto);
            return rangeDto;
        });
    }

    @Override
    public IPage<RangeDto> getRangeByType(Integer page, Integer size, String type) {

        Integer teacherId = teacherService.getTeacherId();

        IPage<Range> myPage = new Page<>(page, size);
        IPage<Range> iPage = this.page(myPage, new QueryWrapper<Range>()
                .eq("file_type", type)
                .eq("teacher_id", teacherId)
                .orderByDesc("upload_time"));
        return iPage.convert(u -> {
            RangeDto rangeDto = new RangeDto();
            BeanUtils.copyProperties(u, rangeDto);
            return rangeDto;
        });
    }

    @Override
    public void uploadRange(UploadRangeForm form) {

        Integer teacherId = teacherService.getTeacherId();

        Range range = BeanUtil.copyProperties(form, Range.class);
        range.setTeacherId(teacherId);
        range.setStatus("ENABLE");
        range.setUploadTime(LocalDateTime.now());

        String type = form.getFileType();// 文件类型
        String filePath = form.getFilePath();// 文件原生路径 http://43.136.111.122:8090/files/1679732383083@index.html
        String rawPath = PathUtil.http2raw(filePath);// 文件存放路径（移动前）
        this.checkFile(rawPath);// 判断文件是否存在
        String savePath = "";// 文件存放路径（移动后）
        if("other".equals(type)) {// 1.上传的是普通文件
            // 确定目标路径 /mnt/nfs_share/upload/temp/files/xxx.zip
            // 转换后路径 /mnt/nfs_share/upload/files/xxx.zip
            savePath = rawPath.replace("/temp/", "/");
            this.move(rawPath, savePath);
            savePath = form.getFilePath().replace("/files/", "/file/");// 保存在数据库中的路径
        } else if ("docker".equals(type)) {// 2.上传的是docker镜像文件
            // 判断文件后缀
            if (!rawPath.endsWith(".tar")) throw new WebException(DOCKER_FILE_TYPE_INCORRECT);
            savePath = rawPath.replace("/temp/files/", "/images/");
            this.move(rawPath, savePath);
            // 镜像名和镜像id
            String dockerName = this.getDockerName(savePath);
            range.setImageName(dockerName);
            String dockerId = this.getDockerId(dockerName);
            range.setImageId(dockerId);
        } else if ("war".equals(type)) {// 3.上传的是war包
            if (!rawPath.endsWith(".zip")) throw new WebException(WAR_ZIP_FILE_TYPE_INCORRECT);
            savePath = rawPath.replace("/temp/files/", "/war/");
            this.move(rawPath, savePath);
        } else if ("zip".equals(type)) {// 4.上传的是php包
            if (!rawPath.endsWith(".zip")) throw new WebException(PHP_ZIP_FILE_TYPE_INCORRECT);
            savePath = rawPath.replace("/temp/files/", "/php/");
            this.move(rawPath, savePath);
        }else{
            throw new WebException(FILE_TYPE_INCORRECT);
        }
        range.setFilePath(savePath);// 更新range
        this.save(range);
    }


    private void move(String oldPath, String newPath){
        Shell shell = ShellUtil.getShell(jarHost, jarUsername, jarPassword).getShell();
        shell.execCommand("mv " + oldPath + " " + newPath);
    }

    public String getDockerName(String tarPath){

        // 转换为docker服务器的路径
        tarPath = tarPath.replace("/nfs_share/", "/local_share/");
        // load tar file to image
        DockerUtil docker = new DockerUtil(host, username, password);
        //DockerUtil docker = new DockerUtil("43.136.98.253", "root", "woniuCQ2023!@#$%^&*()");
        Image image = docker.getImage();
        String loadRes = image.load(tarPath);
        String[] split = loadRes.split("\n");
        String s = split[split.length - 1];// Loaded image: hello-world:latest
        return s.replace("Loaded image: ", "");// TODO 考虑重名情况
    }

    public String getDockerId(String imageName){
        imageName = imageName.split(":")[0];
        DockerUtil docker = new DockerUtil(host, username, password);
        //DockerUtil docker = new DockerUtil("43.136.98.253", "root", "woniuCQ2023!@#$%^&*()");
        Image image = docker.getImage();
        String imageInfo = image.getImageInfo(imageName);// 获取某个镜像的信息
        String[] infos = imageInfo.split(" ");
        ArrayList<String> strs = new ArrayList<>();
        for (String info : infos)
            if(info.length()>0) strs.add(info);
        if(strs.size() < 2 || strs.size() > 8) System.out.println(strs);
        String dockerId = strs.get(2);
        System.out.println(dockerId);
        return dockerId;
    }

    // 上传靶场时，判断文件是否存在
    private void checkFile(String path){
        // 传入示例：/mnt/nfs_share/upload/temp/files/xxx.zip
        Shell shell = new Shell(jarHost, jarUsername, jarPassword);
        String exist = shell.execCommand("[ -f " + path + " ] && echo yes || echo no");
        exist = exist.replace("\n", "");
        if (!exist.equals("yes")) throw new WebException(FILE_IS_NOT_EXISTS);
    }


}
